package main

import (
	"fmt"
	"reflect"
)

// golang的断言 Assertion 和 反射 reflect

type User struct {
	Name string
	Age  int
	Sex  bool
}

type Student struct {
	Class string
	User
}

func (u User) SayName(name string) {
	fmt.Println("hello", name)
}

func demo12() {
	u := User{
		Name: "奇妙",
		Age:  18,
		Sex:  true,
	}
	s := Student{"三年级二班", User{Name: "tom"}}

	fmt.Println("************* 断言 ****************")

	check(u)
	check(s)

	fmt.Println("************* 反射 读取  ****************")
	check1(s)

	fmt.Println("************* 反射 操作数据  ****************")
	// 需要传递指针
	check2(&s)
	fmt.Println(s.Class)

	fmt.Println("************* 反射 调用函数  ****************")
	check3(u)
}

// 反射调用函数
func check3(inter interface{}) {
	v := reflect.ValueOf(inter)
	m := v.Method(0)
	fmt.Println(m) // 获取方法 下标0
	m.Call([]reflect.Value{reflect.ValueOf("月亮之上")})
}

// 反射操作数据
func check2(inter interface{}) {
	v := reflect.ValueOf(inter)
	e := v.Elem()
	e.FieldByName("Class").SetString("dddddddd")
	fmt.Println(inter)
}

// 反射 在编译时不知道类型情况下 可更新变量、运行时查看值、调用方法以及直接对他们的布局进行操作的机制 成为反射。
//
//	可以知道本数据的原始数据类型 内容 方法等
func check1(inter interface{}) {
	t := reflect.TypeOf(inter)  // 获取到类型
	v := reflect.ValueOf(inter) // 获取到值

	fmt.Println("\t ************** 获取到类型和值 **************")
	fmt.Println(t, v)

	fmt.Println("\t ************** 循环迭代 **************")
	// 循环获取
	for i := 0; i < t.NumField(); i++ {
		fmt.Println(i, v.Field(i))
	}

	fmt.Println("\t ************** 获取指定值 参数名 或 指定层级 **************")
	// 获取到指定层级的值
	fmt.Println(v.FieldByIndex([]int{1, 0}))
	fmt.Println(v.FieldByName("Class"))

	fmt.Println("\t ************** 获取类型 **************")
	ty := t.Kind()
	fmt.Println(ty)
	// 需要用 reflect.Struct 里面做了一些值映射处理
	if ty == reflect.Struct {
		fmt.Println("\t 我是结构体")
	}
}

// 断言 直接强制声明变量是什么类型 或通过 v.(type)获取
func check(v interface{}) {
	// 方式1： // 变量.(类型)
	//fmt.Println(v.(User).Name)
	//v.(User).SayName("ddd")

	fmt.Println("************ switch 类型判断 *******************")

	// 方式2  判断类型
	switch v.(type) {
	case User:
		fmt.Println(v.(User).Name)
		fmt.Println("我是 user")
	case Student:
		fmt.Println(v.(Student).Class)
		fmt.Println("我是 student")
	}
}
